/*
 * Copyright (C) 2025 Tsingteng MicroSystem
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @file rw_ci.c
 * @brief China ID Card implementation
 */

#include "nci_feature.h"
#if IS_FEATURE_ON(NCI_FEATURE_RW_CI)

#include "rw_tag.h"
#include "rw_int.h"

#define CI_GET_ATTRIB_RSP_LEN 1
#define CI_GET_UID_RSP_LEN 10
#define CI_PRESENCE_CHECK_RSP_LEN 2

#define CI_UID_LEN 8

typedef struct {
    uint8_t uid[CI_UID_LEN];
} tCI_INFO;

static tCI_INFO s_ci_info;

static tNCI_STATUS ci_transmit(uint8_t *p_cmd, uint8_t cmd_len, uint8_t expect_rsp_len, tNCI_BUF **pp_rsp) {
    tNCI_STATUS ret = NCI_STATUS_EXT_FAILED;
    tNCI_BUF *cmd = alloc_nci_buf(cmd_len);
    tNCI_BUF *rsp = *pp_rsp;
    if (rsp) {
        reset_nci_buf(cmd);
    } else {
        rsp = alloc_nci_buf(0);
    }
    if (!cmd || !rsp) {
        NFCLOG_E("alloc_nci_buf failed");
        ret = NCI_STATUS_EXT_ALLOC_FAILED;
        goto failed;
    }
    ARRAY_TO_BUF(cmd, p_cmd, cmd_len);
    ret = rw_transmit(cmd, rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("rw_transmit failed, ret = %d", ret);
        goto failed;
    }
    if (rsp->len < (expect_rsp_len + 1)) {
        NFCLOG_E("rsp length error, len=%d, expect len=%d", rsp->len, expect_rsp_len + 1);
        ret = NCI_STATUS_EXT_FAILED;
        goto failed;
    }
    if (rsp->data[rsp->len - 1] != 0x00) {
        NFCLOG_E("rsp error, status = 0x%02X", rsp->data[rsp->len - 1]);
        ret = NCI_STATUS_EXT_FAILED;
        goto failed;
    }
    goto cleanup;
failed:
    free_nci_buf(rsp);
    rsp = NULL;
cleanup:
    free_nci_buf(cmd);
    *pp_rsp = rsp;
    return ret;
}

static tNCI_STATUS ci_get_uid(uint8_t *p_uid) {
    tNCI_STATUS ret = NCI_STATUS_EXT_FAILED;
    tNCI_BUF *rsp = NULL;

    uint8_t get_attrib_cmd[] = {0x1D, p_uid[0], p_uid[1], p_uid[2], p_uid[3], 0x00, 0x08, 0x01, 0x08};
    ret = ci_transmit(get_attrib_cmd, sizeof(get_attrib_cmd), CI_GET_ATTRIB_RSP_LEN, &rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("ci_transmit failed, ret = %d", ret);
        goto cleanup;
    }

    uint8_t attrib_res = rsp->data[0];

    uint8_t get_uid_cmd[] = {0x00, 0x36, 0x00, 0x00, attrib_res};
    ret = ci_transmit(get_uid_cmd, sizeof(get_uid_cmd), CI_GET_UID_RSP_LEN, &rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("ci_transmit failed, ret = %d", ret);
        goto cleanup;
    }
    uint16_t sw = (uint16_t)(rsp->data[CI_GET_UID_RSP_LEN - 2] << 8) | rsp->data[CI_GET_UID_RSP_LEN - 1];
    if (sw != 0x9000) {
        NFCLOG_E("get uid failed, sw = 0x%04X", sw);
        ret = NCI_STATUS_EXT_FAILED;
        goto cleanup;
    }
    if (!osi_memcpy_s(s_ci_info.uid, CI_UID_LEN, rsp->data, CI_UID_LEN)) {
        NFCLOG_E("copy uid failed");
        ret = NCI_STATUS_EXT_COPY_FAILED;
        goto cleanup;
    }
cleanup:
    free_nci_buf(rsp);
    return ret;
}

static tNCI_STATUS presence_check() {
    tNCI_STATUS ret = NCI_STATUS_EXT_FAILED;
    tNCI_BUF *rsp = NULL;

    uint8_t presence_check_cmd[] = {0xB2};
    ret = ci_transmit(presence_check_cmd, sizeof(presence_check_cmd), CI_PRESENCE_CHECK_RSP_LEN, &rsp);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("ci_transmit failed, ret = %d", ret);
        goto cleanup;
    }
    // 只要有回复数据，就认为卡存在
cleanup:
    free_nci_buf(rsp);
    return ret;
}

static tNCI_STATUS on_connect(tNCI_RW_TAG *p_tag) {
    tNCI_STATUS ret = ci_get_uid(p_tag->uid);
    if (ret != NCI_STATUS_OK) {
        NFCLOG_E("ci_get_uid failed, ret = %d", ret);
        return ret;
    }

    if (!osi_memcpy_s(p_tag->uid, CI_UID_LEN, s_ci_info.uid, CI_UID_LEN)) {
        NFCLOG_E("copy uid failed");
        return NCI_STATUS_EXT_COPY_FAILED;
    }
    p_tag->uid_len = CI_UID_LEN;
    return NCI_STATUS_OK;
}

static tNCI_STATUS on_disconnect() {
    return NCI_STATUS_OK;
}

tNCI_RW_TAG_API g_rw_ci_api = {
    .max_retry_count = NCI_RW_DEFAULT_MAX_RETRY_COUNT,
    .on_connect = on_connect,
    .on_disconnect = on_disconnect,
    .presence_check = presence_check,
};

#endif // IS_FEATURE_ON(NCI_FEATURE_RW_CI)
